﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using XNAGuiLib.Core.ActionHandling;
using XNAGuiLib.Core.Utilities;

namespace XNAGuiLib.Core
{
    public class GIComponent : Component, IDrawable, IGameComponent, IUpdateable, IAction, IComparable<GIComponent>
    {

        #region GIComponent Protected Field - _game;
        protected Game _game;
        #endregion

        #region GIComponent Protected Field - _ms;
        protected MouseState _ms;
        #endregion

        #region GIComponent Protected Field - _oms;
        protected MouseState _oms;
        #endregion

        #region GIComponent Protected Field - _gics;
        protected List<GIComponent> _gics = new List<GIComponent>();
        #endregion


        #region GIComponent Protected Field - _mouseActions;
        protected List<MouseAction> _mouseActions = new List<MouseAction>();
        #endregion

        #region GIComponent Protected Field - _keyActions;
        protected List<KeyAction> _keyActions = new List<KeyAction>();
        #endregion



        #region GIComponent Public Field - AutoScale
        public bool AutoScale
        {
            get { return this.AutoScaleWidth && this.AutoScaleHeight; }
            set
            {
                this.AutoScaleWidth = value;
                this.AutoScaleHeight = value;
            }
        }
        #endregion

        #region GIComponent Public Field - AutoScaleWidth
        protected bool _autoScaleWidth = true;
        /// <summary>
        /// gets or sets if the panels content should be auto scaled in width or not
        /// </summary>
        public bool AutoScaleWidth
        {
            get { return this._autoScaleWidth; }
            set { this._autoScaleWidth = value; }
        }
        #endregion

        #region GIComponent Public Field - AutoScaleHeight
        protected bool _autoScaleHeight = true;
        /// <summary>
        /// gets or sets if the panels content should be auto scaled in height or not
        /// </summary>
        public bool AutoScaleHeight
        {
            get { return this._autoScaleHeight; }
            set { this._autoScaleHeight = value; }
        }
        #endregion


        #region GIComponent Public Field - Bounds
        /// <summary>
        /// gets or sets the width and the height of the component
        /// </summary>
        public Vector2 Bounds
        {
            get { return new Vector2(this._width, this._height); }
            set
            {
                this.Resize((int)value.X, (int)value.Y);
                this.UpdateSize();
            }
        }
        #endregion

        #region GIComponent Public Field - Width
        protected int _width;
        /// <summary>
        /// gets the total width of the component
        /// </summary>
        public int Width
        {
            get { return this._width; }
        }
        #endregion

        #region GIComponent Public Field - Height
        protected int _height;
        /// <summary>
        /// gets the total height of the component
        /// </summary>
        public int Height
        {
            get { return this._height; }
        }
        #endregion


        #region GIComponent Public Field - Rectangle
        /// <summary>
        /// gets the rectangle of the component with x- and y-coordinates and width and height
        /// </summary>
        public Rectangle Rectangle
        {
            get { return new Rectangle((int)this.TotalPosition.X, (int)this.TotalPosition.Y, this.Width, this.Height); }
        }
        #endregion

        #region GIComponent Public Field - ContentComponents
        protected List<ContentComponent> _content = new List<ContentComponent>();
        /// <summary>
        /// gets the list of ContentComponents in a 
        /// </summary>
        public List<ContentComponent> ContentComponents
        {
            get { return _content; }
        }
        #endregion



        #region IDrawable Fields
        #region IDrawable Field - DrawOrder
        protected int _drawOrder = 0;
        /// <summary>
        /// gets the drawOrder of the GIComponent
        /// </summary>
        public int DrawOrder
        {
            get { return _drawOrder; }
        }
        #endregion

        #region IDrawable Field - DrawOrderChanged
        /// <summary>
        /// event handler fired when the draw order is changed
        /// </summary>
        public event EventHandler DrawOrderChanged;
        #endregion

        #region IDrawable Field - Visible
        /// <summary>
        /// gets or sets if the component is visible or not
        /// </summary>
        public bool Visible
        {
            get { return base._visible; }
            set { base._visible = value; }
        }
        #endregion

        #region IDrawable Field - VisibleChanged
        /// <summary>
        /// event handler fired when the visibility is changed
        /// </summary>
        public event EventHandler VisibleChanged;
        #endregion
        #endregion

        #region IUpdateable Fields
        #region IUpdateable Field - Enabled
        /// <summary>
        /// gets or sets if the component is enabled (means actions will be executed)
        /// </summary>
        public bool Enabled
        {
            get { return base._enabled; }
            set { base._enabled = value; }
        }
        #endregion

        #region IUpdateable Field - EnabledChanged
        /// <summary>
        /// event handler fired when the enability is changed
        /// </summary>
        public event EventHandler EnabledChanged;
        #endregion

        #region IUpdateable Field - UpdateOrder
        protected int _updateOrder;
        /// <summary>
        /// gets the updateOrder of the GIComponent
        /// </summary>
        public int UpdateOrder
        {
            get { return _updateOrder; }
        }
        #endregion

        #region IUpdateable Field - UpdateOrderChanged
        /// <summary>
        /// event handler fired when the update order is changed
        /// </summary>
        public event EventHandler UpdateOrderChanged;
        #endregion
        #endregion


        public GIComponent(Game game)
        {
            _game = game;
        }

        // updates the width and height at once
        #region GIComponent Protected Method - UpdateSize()
        /// <summary>
        /// updates both width and height
        /// </summary>
        protected void UpdateSize()
        {
            UpdateWidth();
            UpdateHeight();
            // Console.WriteLine("Width: " + this._width + " to Height: " + this._height);
        }
        // updates the width
        #region GIComponent Protected Method - UpdateWidth()
        /// <summary>
        /// updates the width of the GIComponent
        /// </summary>
        protected void UpdateWidth()
        {
            this._width = 0;
            foreach (ContentComponent contCmp in _content)
            {
                if (contCmp.MaxWidth() > this._width)
                    this._width = contCmp.MaxWidth();
            }
            foreach (GIComponent gic in _gics)
            {
                gic.UpdateWidth();
                if (gic.Width > this._width)
                    this._width = gic.Width;
            }
        }
        #endregion
        // updates the height
        #region GIComponent Protected Method - UpdateHeight()
        /// <summary>
        /// updates the height of the GIComponent
        /// </summary>
        protected void UpdateHeight()
        {
            this._height = 0;
            foreach (ContentComponent contCmp in _content)
            {
                if (contCmp.MaxHeight() > this._height)
                    this._height = contCmp.MaxHeight();
            }

            foreach (GIComponent gic in _gics)
            {
                gic.UpdateHeight();
                if (gic.Height > this._height)
                    this._height = gic.Height;
            }
        }
        #endregion
        #endregion
        // moves the component with the movement of the mouse
        #region GIComponent Protected Method - MoveWithMouse()
        /// <summary>
        /// moves the component with the movement of the mouse
        /// </summary>
        protected void MoveWithMouse()
        {
            // get the current mouse position
            Vector2 mousePos = new Vector2(_ms.X, _ms.Y);
            // get the mouse position from the last update cycle
            Vector2 mousePosOld = new Vector2(_oms.X, _oms.Y);

            Vector2 tempRelativePosition = this.RelativePosition;

            this.RelativePosition += (mousePos - mousePosOld);


            if (this.RelativePosition.X < 0)
                this.RelativePosition = new Vector2(0, this.RelativePosition.Y);
            else if ((this.RelativePosition.X + this.Width) > this._game.Window.ClientBounds.Width)
                this.RelativePosition = new Vector2((this._game.Window.ClientBounds.Width - this.Width), this.RelativePosition.Y);

            if (this.RelativePosition.Y < 0)
                this.RelativePosition = new Vector2(tempRelativePosition.X, 0);
            else if ((this.RelativePosition.Y + this.Height) > this._game.Window.ClientBounds.Height)
                this.RelativePosition = new Vector2(this.RelativePosition.X, (this._game.Window.ClientBounds.Height - this.Height));

        }
        #endregion
        // moves the component on the x-axxis
        #region GIComponent Protected Method - MoveX(int amount)
        /// <summary>
        /// moves the component by the amount on the x-axxis (left and right)
        /// </summary>
        /// <param name="amount">the number of pixel to move</param>
        protected void MoveX(int amount)
        {
            this.RelativePosition += new Vector2(amount, 0);
        }
        #endregion
        // moves the component on the y-axxis
        #region GIComponent Protected Method - MoveY(int amount)
        /// <summary>
        /// moves the component by the amount on the y-axxis (up and down)
        /// </summary>
        /// <param name="amount">the number of pixel to move</param>
        protected void MoveY(int amount)
        {
            this.RelativePosition += new Vector2(0, amount);
        }
        #endregion




        // adds another GIComponent-object to the component
        #region GIComponent Protected Method - Add(GIComponent gic)
        /// <summary>
        /// adds the GIC to the components GIC-list
        /// </summary>
        /// <param name="gic">GIC to add as a child</param>
        protected void Add(GIComponent gic)
        {
            gic.Owner = this;
            _gics.Add(gic);
        }
        #endregion
        // removes a GIComponent-object from the component
        #region GIComponent Protected Method - Remove(GIComponent gic)
        /// <summary>
        /// removes the gic from the components GIC-list
        /// </summary>
        /// <param name="gic">GIC to remove</param>
        /// <returns>true if successful, false otherwise</returns>
        protected bool Remove(GIComponent gic)
        {
            if (this._gics.Contains(gic))
                return this._gics.Remove(gic);
            return false;
        }
        #endregion
        // unbinds a GIComponent-object from the component
        #region GIComponent Protected Method - UnbindGIC(int index)
        /// <summary>
        /// unbinds/frees a GIC from the component
        /// </summary>
        /// <param name="index">index of the GIC to free</param>
        /// <returns>ownerless GIComponent</returns>
        protected GIComponent UnbindGIC(int index)
        {
            if (_gics.Count > index)
            {
                GIComponent gic = this._gics[index];
                this._gics.RemoveAt(index);

                gic.Owner = null;
                return gic;
            }
            else return null;
        }
        #endregion


        // Virtual Method - Resize(int width, int height)
        #region GIComponent Protected Virtual Method - Resize(int width, int height)
        protected virtual void Resize(int width, int height)
        {
            this._width = width;
            this._height = height;
        }
        #endregion


        // adds the given ContentComponent
        #region GIComponent Public Method - Add(ContentComponent item)
        /// <summary>
        /// adds a ContentComponent to the GIComponent
        /// </summary>
        /// <param name="item">the ContentComponent object to add</param>
        public void Add(ContentComponent item)
        {
            item.Owner = this;
            this._content.Add(item);
            this.UpdateSize();
        }
        #endregion
        // removes the given ContentComponent
        #region GIComponent Public Method - Remove(ContentComponent item)
        /// <summary>
        /// removes the given ContentComponent from the GIComponent (if exists)
        /// </summary>
        /// <param name="item">the ContentComponent to remove</param>
        public bool Remove(ContentComponent item)
        {
            bool retVal = false;
            if (this._content.Contains(item))
            {
                retVal = this._content.Remove(item);
                this.UpdateSize();
            }
            return retVal;

        }
        #endregion
        // removes the ContentComponent at the given index
        #region GIComponent Public Method - RemoveAt(int index)
        /// <summary>
        /// removes the ContentComponent at the given index from the list
        /// </summary>
        /// <param name="index">the index from which the ContentComponent should be removed</param>
        public void RemoveAt(int index)
        {
            if ((this._content.Count - 1) >= index)
                this._content.RemoveAt(index);

            this.UpdateSize();
        }
        #endregion
        // removes the last ContentComponent
        #region GIComponent Public Method - RemoveLast()
        /// <summary>
        /// removes the last ContentComponent from the list
        /// </summary>
        public void RemoveLast()
        {
            if (this._content.Count >= 1)
                this._content.RemoveAt(this._content.Count - 1);

            this.UpdateSize();
        }
        #endregion
        // frees a ContentComponent from this object
        #region GIComponent Public Method - UnbindContent(int index)
        /// <summary>
        /// unbinds/frees a ContentComponent from the GIComponent
        /// </summary>
        /// <param name="index">the index on which the ContentComponent should be set free</param>
        /// <returns>the free ContentComponent taken from the GIComponent</returns>
        public ContentComponent UnbindContent(int index)
        {
            ContentComponent cont = this._content[index];
            this._content.RemoveAt(index);
            this.UpdateSize();

            cont.Owner = null;
            return cont;
        }
        #endregion


        // adds a KeyAction object to the GIC
        #region GIComponent Public Method - Add(KeyAction action)
        /// <summary>
        /// adds a key Action to the GIComponent
        /// </summary>
        /// <param name="action">KeyAction to add</param>
        public void Add(KeyAction action)
        {
            this._keyActions.Add(action);
        }
        #endregion
        // adds a MouseAction object to the GIC
        #region GIComponent Public Method - Add(MouseAction action)
        /// <summary>
        /// adds a mouse Action to the GIComponent
        /// </summary>
        /// <param name="action">MouseAction to add</param>
        public void Add(MouseAction action)
        {
            this._mouseActions.Add(action);
        }
        #endregion

        // removes a KeyAction object from the GIC
        #region GIComponent Public Method - Remove(KeyAction action)
        /// <summary>
        /// removes an KeyAction object from the GIC
        /// </summary>
        /// <param name="action">KeyAction to add to the GIC</param>
        /// <returns>true if it was removed, false otherwise</returns>
        public bool Remove(KeyAction action)
        {
            if (this._keyActions.Contains(action))
                return this._keyActions.Remove(action);
            return false;
        }
        #endregion
        // removes a MouseAction object from the GIC
        #region GIComponent Public Method - Remove(MouseAction action)
        /// <summary>
        /// removes an MouseAction object from the GIC
        /// </summary>
        /// <param name="action">MouseAction to add to the GIC</param>
        /// <returns>true if it was removed, false otherwise</returns>
        public bool Remove(MouseAction action)
        {
            if (this._mouseActions.Contains(action))
                return this._mouseActions.Remove(action);
            return false;
        }
        #endregion

        // unbinds the KeyAction on the index from the KeyAction list
        #region GIComponent Public Method - UnbindKeyAction(int index)
        /// <summary>
        /// unbinds/frees an KeyAction object from its invoker GIC
        /// </summary>
        /// <param name="index">index to free from the GICs KeyAction list</param>
        /// <returns>the KeyAction object from the Action list</returns>
        public KeyAction UnbindKeyAction(int index)
        {
            if (_keyActions.Count > index)
            {
                KeyAction action = this._keyActions[index];
                this._keyActions.RemoveAt(index);

                action.Free();
                return action;
            }
            else return null;
        }
        #endregion
        // unbinds the MouseAction on the index from the MouseAction list
        #region GIComponent Public Method - UnbindMouseAction(int index)
        /// <summary>
        /// unbinds/frees an MouseAction object from its invoker GIC
        /// </summary>
        /// <param name="index">index to free from the GICs MouseAction list</param>
        /// <returns>the MouseAction object from the Action list</returns>
        public MouseAction UnbindMouseAction(int index)
        {
            if (_mouseActions.Count > index)
            {
                MouseAction action = this._mouseActions[index];
                this._mouseActions.RemoveAt(index);

                action.Free();
                return action;
            }
            else return null;
        }
        #endregion

        // set the opaque of the whole gic (only ContentComponent not child-GICs)
        #region GIComponent Public Method - Opaque(int percentage)
        /// <summary>
        /// set the opaque of the whole gic (only ContentComponent not child-GICs)
        /// </summary>
        /// <param name="percentage">degree of visibility/opaque</param>
        public void Opaque(int percentage)
        {
            if (percentage >= 0 && percentage <= 100)
            {
                foreach (ContentComponent cc in _content)
                {
                    if (cc.ContTexture != null)
                        cc.ContTexture.ModulationColor = ColorBlend.ModulateAlpha(cc.ContTexture.ModulationColor, (byte)(2.55 * percentage));
                    if (cc.ContText != null)
                        cc.ContText.TextColor = ColorBlend.ModulateAlpha(cc.ContText.TextColor, (byte)(2.55 * percentage));
                }
            }
        }
        #endregion



        // Virtual Method - ExecuteCommand(string command)
        #region GIComponent Public Virtual Method - ExecuteCommand(string command)
        public virtual void ExecuteCommand(string command)
        {
            #region ExecuteCommand | CMD_SHOW
            if (command.Equals(ActionCommand.CMD_SHOW))
            {
                this._visible = true;
            }
            #endregion

            #region ExecuteCommand | CMD_HIDE
            else if (command.Equals(ActionCommand.CMD_HIDE))
            {
                this._visible = false;
            }
            #endregion

            #region ExecuteCommand | CMD_TGL_VISIBILITY
            else if (command.Equals(ActionCommand.CMD_TGL_VISIBILITY))
            {
                this._visible = !this._visible;
            }
            #endregion

            #region ExecuteCommand | CMD_ENABLE
            else if (command.Equals(ActionCommand.CMD_ENABLE))
            {
                this._enabled = true;
            }
            #endregion

            #region ExecuteCommand | CMD_DISABLE
            else if (command.Equals(ActionCommand.CMD_DISABLE))
            {
                this._enabled = false;
            }
            #endregion

            #region ExecuteCommand | CMD_TGL_ENABILITY
            else if (command.Equals(ActionCommand.CMD_TGL_ENABILITY))
            {
                this._enabled = !this._enabled;
            }
            #endregion

            #region ExecuteCommand | CMD_MS_MOVE
            else if (command.Equals(ActionCommand.CMD_MOVE) && this._visible)
            {
                MoveWithMouse();
            }
            #endregion

        }
        #endregion

        // draws the GIComponent and all its children
        #region GIComponent Public Override Method - Draw(SpriteBatch spriteBatch)
        /// <summary>
        /// draws the GIComponent and all its children
        /// </summary>
        /// <param name="spriteBatch">SpriteBatch to use for drawing</param>
        public override void Draw(SpriteBatch spriteBatch)
        {
            if (_visible)
            {
                spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
                foreach (ContentComponent content in this._content)
                {
                    content.Draw(spriteBatch);
                }
                spriteBatch.End();

                foreach (GIComponent gic in this._gics)
                {
                    gic.Draw(spriteBatch);
                }
            }
        }
        #endregion


        // ==============================================================   
        // implemented methods for all interfaces inherited by this class
        // ==============================================================

        // IDrawable Method - Draw(GameTime gameTime)
        #region IDrawable Method - Draw(GameTime gameTime)
        public virtual void Draw(GameTime gameTime)
        {
            if (_visible)
            {
                SpriteBatch spriteBatch = Provider.GetInstance(this._game).GetSpriteBatch();
                spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
                foreach (ContentComponent content in this._content)
                {
                    content.Draw(spriteBatch);
                }
                spriteBatch.End();

                foreach (GIComponent gic in this._gics)
                {
                    gic.Draw(spriteBatch);
                }
            }
        }
        #endregion

        // IGameComponent Method - Initialize()
        #region IGameComponent Method - Initialize()
        public void Initialize()
        {

        }
        #endregion

        // IUpdateable Method - Update(GameTime gameTime)
        #region IUpdateable Method - Update(GameTime gameTime)
        public virtual void Update(GameTime gameTime)
        {
            _ms = Mouse.GetState();

            if (this._enabled)
            {

                foreach (KeyAction action in this._keyActions)
                {
                    action.ExecuteCheck(_ms, Keyboard.GetState());
                }

                foreach (MouseAction action in this._mouseActions)
                {
                    action.ExecuteCheck(_ms, Keyboard.GetState());
                }
            }

            foreach (GIComponent gic in _gics)
            {
                gic.Update(gameTime);
            }

            _oms = Mouse.GetState();
        }
        #endregion

        // IAction Method - Execute(string command)
        #region IAction Method - Execute(string command)
        public void Execute(string command)
        {
            ExecuteCommand(command);
        }
        #endregion

        // IComparable Method - CompareTo(GIComponent other)
        #region IComparable Method - CompareTo(GIComponent other)
        public int CompareTo(GIComponent other)
        {
            return this._drawOrder.CompareTo(other._drawOrder);
        }
        #endregion

    }
}